//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Eas.WebCrawler.Interfaces;
using Eas.WebCrawler.Model.Core;

namespace Eas.WebCrawler.Model.Events {
    internal class BaseWebCrawlerEvents : Eas.WebCrawler.Interfaces.WebCrawlerEvents, EventReporter {
        Eas.WebCrawler.Common.Diagnostics.DeliTicketDispenser _ticketDispenser = new Eas.WebCrawler.Common.Diagnostics.DeliTicketDispenser();

        private TraceLevel _TraceLevel = TraceLevel.None;
        private int _TraceLevelAsInt = (int)TraceLevel.None;

        public TraceLevel TraceLevel {
            get { return _TraceLevel; }
            set { 
                _TraceLevel = value;
                _TraceLevelAsInt = (int)_TraceLevel;
            }
        }


        #region Eas.WebCrawler.Interfaces.WebCrawlerEvents Implementation
        
        /// <summary>Fired when the BeforeStart event occurs.</summary>
        public event System.EventHandler Starting;
        
        /// <summary>Fired when the AfterStop event occurs.</summary>
        public event System.EventHandler Stopped;
        
        /// <summary>Fired when the BeforeUnPause event occurs.</summary>
        public event System.EventHandler Unpausing;
        
        /// <summary>Fired when the AfterPause event occurs.</summary>
        public event System.EventHandler Paused;
        
        /// <summary>Fired when the UrlProcessed event occurs.</summary>
        public event System.EventHandler<UrlCompletedEventArgs> UrlProcessed;
        
        /// <summary>Fired when the Trace event occurs.</summary>
        public event System.EventHandler<ProcessingStateMessageEventArgs> OnTrace;
        
        /// <summary>Fired when the Error event occurs.</summary>
        public event System.EventHandler<ProcessingStateMessageEventArgs> OnError;

        /// <summary>Fired when the UrlFailed event occurs.</summary>
        public event System.EventHandler<UrlFailedEventArgs> UrlFailed;

        /// <summary>Fired when the UrlBeganProcessing event occurs.</summary>
        public event System.EventHandler<ProcessingStateEventArgs> UrlBeganProcessing;

        #endregion

        #region EventReporter Members

        public void ReportWebRequestStarted(ProcessingState state) {
            Fire_UrlBeganProcessing(state);
        }

        public void ReportStarting() {
            Fire_Starting();
        }

        public void ReportStopped() {
            Fire_Stopped();
        }

        public void ReportUnpausing() {
            Fire_UnPausing();
        }

        public void ReportPaused() {
            Fire_Paused();
        }
        
        public void ReportTrace(TraceLevel level, ProcessingState state, string message, params object[] arguments) {
            if ((int)level <= _TraceLevelAsInt) {
                if (state.Request!= null && state.Request.Uri!=null)
                    Trace(state, state.Request.Uri.ToString(), message, arguments);
                else
                    Trace(state, "none", message, arguments);
            }
        }

        public void ReportError(ProcessingState state, string prefix, System.Exception exception) {
            ReportTrace(TraceLevel.Critical, state, exception.ToString());
            
            string sUri = "none";
            if (state.Request != null && state.Request.Uri != null)
                sUri = state.Request.Uri.ToString();
            
            string prefixSeperator = ":\t";
            if (string.IsNullOrEmpty(prefix))
                prefixSeperator = string.Empty;

            Error(state, "{0}:\t{1}{2}{3}", sUri, prefix, prefixSeperator, exception.Message);
        }

        public void ReportWebResponseFailure(ProcessingState state, int responseCode, string message) {
            string errorText = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:\t{1}:\t{2}", state.Request.Uri, responseCode, message);
            Fire_UrlFailed(new UrlFailedEventArgs( state, errorText, responseCode));
        }

        public void ReportRequestComplete(ProcessingState state, Eas.WebCrawler.Interfaces.TemporaryStorage storage) {
            Fire_UrlProcessed(new Eas.WebCrawler.Interfaces.UrlCompletedEventArgs(state, storage));
        }

        #endregion

        #region WorkerComponent Members

        public void Initialize(WorkerContext context) {
            
        }

        #endregion

        #region Methods For Firing Events

        /// <summary>Fires when the Starting event.</summary>
        public void Fire_Starting() {
            if (Starting != null) {
                Delegate[] delegates = Starting.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler handler = (EventHandler)delegates[i];
                    try {
                        handler(this, new EventArgs());
                    }
                    catch (System.Exception exception) {
                        ReportError(BaseProcessingState.EmptyState, "Error in BeforeStart handler", exception);
                    }
                }
            }
        }

        /// <summary>Fires when the Stopped event.</summary>
        public void Fire_Stopped() {
            if (Stopped != null) {
                Delegate[] delegates = Stopped.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler handler = (EventHandler)delegates[i];
                    try{
                        handler(this, new EventArgs());
                    }
                    catch(System.Exception exception){
                        ReportError(BaseProcessingState.EmptyState, "Error in AfterStop handler",exception);
                    }
                }
            }
        }

        /// <summary>Fires when the Unpausing event.</summary>
        public void Fire_UnPausing() {
            if (Unpausing != null) {
                Delegate[] delegates = Unpausing.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler handler = (EventHandler)delegates[i];
                    try {
                        handler(this, new EventArgs());
                    }
                    catch (System.Exception exception) {
                        ReportError(BaseProcessingState.EmptyState, "Error in BeforeUnPause handler",exception);
                    }
                }
            }
        }

        /// <summary>Fires when the Paused event.</summary>
        public void Fire_Paused() {
            if (Paused != null) {
                Delegate[] delegates = Paused.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler handler = (EventHandler)delegates[i];
                    try{
                        handler(this, new EventArgs());
                    }
                    catch(System.Exception exception){
                        ReportError(BaseProcessingState.EmptyState, "Error in AfterPause handler",exception);
                    }
                }
            }
        }

        /// <summary>Fires when the UrlProcessed event.</summary>
        public void Fire_UrlProcessed(Eas.WebCrawler.Interfaces.UrlCompletedEventArgs argument) {
            if (UrlProcessed != null) {
                Delegate[] delegates = UrlProcessed.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler<UrlCompletedEventArgs> handler = (EventHandler<UrlCompletedEventArgs>)delegates[i];
                    try {
                        handler(this, argument);
                    }
                    catch (System.Exception exception) {
                        ReportError(BaseProcessingState.EmptyState, "Error in UrlProcessed handler",exception);
                    }
                }
            }
        }

        /// <summary>Fires when the OnTrace event.</summary>
        public void Fire_OnTrace(ProcessingState state, string message) {
            if (OnTrace != null) {
                Delegate[] delegates = OnTrace.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler<ProcessingStateMessageEventArgs> handler = (EventHandler<ProcessingStateMessageEventArgs>)delegates[i];
                    try {
                        handler(this, new ProcessingStateMessageEventArgs(state, message));
                    }
                    catch (System.Exception exception) {
                        Fire_OnError(state, "Error in OnTrace handler: "+exception.ToString());
                        //ReportError(BaseProcessingState.EmptyState, "Error in OnTrace handler",exception);  //Nope, this will cause loop.
                    }
                }
            }
        }

        /// <summary>Fires when the OnError event.</summary>
        public void Fire_OnError(ProcessingState state, string message) {
            if (OnError != null) {
                Delegate[] delegates = OnError.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler<ProcessingStateMessageEventArgs> handler = (EventHandler<ProcessingStateMessageEventArgs>)delegates[i];
                    try {
                        handler(this, new ProcessingStateMessageEventArgs(state, message));
                    }
                    catch (System.Exception) {
                        
                    }
                }
            }
        }

        /// <summary>Fires when the UrlFailed event.</summary>
        private void Fire_UrlFailed(UrlFailedEventArgs argument) {
            if (UrlFailed != null) {
                Delegate[] delegates = UrlFailed.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler<UrlFailedEventArgs> handler = (EventHandler<UrlFailedEventArgs>)delegates[i];
                    try {
                        handler(this, argument);
                    }
                    catch (System.Exception exception) {
                        ReportError(BaseProcessingState.EmptyState, "Error in UrlFailed handler",exception);
                    }
                }
            }
        }

        /// <summary>Fires when the UrlBeganProcessing event.</summary>
        private void Fire_UrlBeganProcessing(ProcessingState state) {
            if (UrlBeganProcessing != null) {
                Delegate[] delegates = UrlBeganProcessing.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++) {
                    EventHandler<ProcessingStateEventArgs> handler = (EventHandler<ProcessingStateEventArgs>)delegates[i];
                    try {
                        handler(this, new ProcessingStateEventArgs(state));
                    }
                    catch (System.Exception exception) {
                        ReportError(BaseProcessingState.EmptyState, "Error in UrlBeganProcessing handler",exception);
                    }
                }
            }
        }


        #endregion

        #region Internal Helper Methods
        //[System.Diagnostics.Conditional("TRACE")]
        void Trace(ProcessingState state, string uri, string baseString, params object[] parameters) {
            StringBuilder builder = new StringBuilder();
            builder.Append(state.Id);
            builder.Append(":\t");
            builder.Append(_ticketDispenser.GetNextAsLong());
            builder.Append(":\t");
            builder.Append(uri);
            builder.Append(":\t");
            builder.AppendFormat(baseString, parameters);
            string message = builder.ToString();
            Fire_OnTrace(state, message);
        }

        void Error(ProcessingState state, string baseString, params object[] parameters) {
            string message = String.Format(System.Globalization.CultureInfo.InvariantCulture, baseString, parameters);
            Fire_OnError(state, message);
        }
        #endregion
    }
}
